from operator import itemgetter, attrgetter
"""
Main file 
"""

g_settings = 0

#Enumerated variables for the roles of the agents

# Convolutions of field_grid. Contain ones where there is a camp spot
# maps have a one on the spot to the n/e/s/w of a sw/nw/se/ne corner
sw_w = None
sw_s = None
nw_w = None
nw_n = None
se_e = None
se_s = None
ne_e = None
ne_n = None
coverSpotsMap = None # OR of the above maps
coverSpots = None # List of cover spots ((x,y), [(dirx,diry), ..])

################################################################################
# Module: UTIL
################################################################################

# Improved A* 
def initAstar(mesh,grid,tilesize=16):
    precalcmesh = {}
    # For each node, calculate path/distance with astar to each other node and add to dict
    for snode in mesh:
        precalcmesh[snode] = {}
        for enode in mesh:
            if line_intersects_grid(snode,enode,grid,tilesize):
                path = find_path(snode, enode, mesh, grid, tilesize)
                precalcmesh[snode][enode] = [snode] + path
            else:
                if enode==snode:
                    precalcmesh[snode][enode] = [enode]
                else:
                    precalcmesh[snode][enode] = [snode] + [enode]
    return precalcmesh

def find_path_Astar(start, end, mesh, precalcmesh, grid, tilesize=16, printer=False):
    if printer: print mesh.keys()
    # Find each visible node for the start
    if not line_intersects_grid(start, end, grid, tilesize):
        return [end]

    snodes = [n for n in mesh if not line_intersects_grid(start,n,grid,tilesize)]

    # Find each visible node for the end
    if end in mesh:
        enodes = [end]
    else:
        enodes = [m for m in mesh if not line_intersects_grid(end,m,grid,tilesize)]

    # Find the set of nodes with smallest distance between them, concatenate path and return

    bestdis = 1000000000
    
    for snode in snodes:
        for enode in enodes:
            path = precalcmesh[snode][enode]

            pathlength = pathLength(snode, path+[end])
            if (pathlength < bestdis):
                bestdis = pathlength 
                bestpath = path + [end]
    
    return bestpath


#def inRangeManhattan(x1, y1, x2, y2, maxRange):
#    return abs(x1 - x2) < maxRange and abs(y1 - y2) < maxRange
def inRangeManhattan(p1, p2, maxRange):
    return abs(p1[0] - p2[0]) < maxRange and abs(p1[1] - p2[1]) < maxRange
def pathLength(loc, path):
    """Calculates the length of a path,
    loc: the location of the agent
    path: a list of points in the path, as returned from find_path
    NOTE: maybe we can get this info from the pathfinding algo itsel?
       Would need to edit Thomas' code and get it accepted for the tournament"""
    l = ((loc[0] - path[0][0])**2 + (loc[1] - path[0][1])**2)**0.5
    for i in range(1,len(path)):
        l += ((path[i-1][0] - path[i][0])**2 + (path[i-1][1] - path[i][1])**2)**0.5
    return l

def stepsToTarget(loc, ang, path, length):
    """Calculates the number of steps required to reach a target,
    TODO: taking into account the angles in the path.
    It uses the distance per step, and the number of times
    a step is wasted on turning because the angle is too large.
    Whether this is exact depends on the agent behaviour:
    does it stop to turn or not?
    loc: current location of agent
    ang: current angle of agent
    path: list of points in the path
    length: length of the path, as calculated by pathLength()
    """

    steps = actualStepsToTarget(loc, ang, path)
    #print "actual = " + str(steps)
    #steps = math.ceil(length / g_settings.max_speed)
    #steps += getTurns(path, loc,  ang)
    return steps


def actualStepsToTarget(loc, ang, path):
    # For each node in path, calculate turns to traverse from the previous node
    # and on each node calculate turns to rotate
    turns = 0
    crt = loc
    crtang = ang

    for node in path:
        # calculate turn towards new node
        turnsteps = math.ceil(abs(getTurn(crt, node, crtang))/ g_settings.max_turn)

        # calculate distance to new node
        travelsteps = point_dist(crt,node) / float(g_settings.max_speed)

        # add to number of turns
        turns += turnsteps + travelsteps

        # Start from new node
        crt = node
        crtang = getRotation(crt, node)

    return turns

def getTurns(path,loc,ang):
    """Returns the number of turns to travel the given path starting
    at loc with angle ang"""
    turnsteps = 0
    #towards first path point
    Turn = abs(getTurn(loc, path[0], ang))
    turnsteps += math.ceil(Turn / g_settings.max_turn)
    
    #for second path point
    if len(path) > 1:
        Turn = abs(getTurn(path[0], path[1], getRotation(loc, path[0])))
        turnsteps += math.ceil(Turn / g_settings.max_turn)
        if len(path) > 2:
            for i in range(2,len(path)-1):
                Turn = abs(getTurn(path[i-1], path[i], getRotation(path[i-2], path[i-1])))
                turnsteps += math.ceil(Turn / g_settings.max_turn)
    return turnsteps

def getRotation(loc, targetloc):
    dx = targetloc[0]-loc[0]
    dy = targetloc[1]-loc[1]
    return math.atan2(dy,dx)

def getTurn(loc, target, ang):
    """
    Gets the rotation required to turn towards a target,
    when standing at loc, with given angle.
    Note: does not truncate turn to the max allowed value,
    so that user code can check if full rotation is possible,
    and choose what to do.
    """
    dx = target[0]-loc[0]
    dy = target[1]-loc[1]
    return angle_fix(math.atan2(dy, dx)-ang)

def permutations(iterable, r=None): # From itertools.permutations(), but not available in 2.5
    # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
    # permutations(range(3)) --> 012 021 102 120 201 210
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    if r > n:
        return
    indices = range(n)
    cycles = range(n, n-r, -1)
    yield tuple(pool[i] for i in indices[:r])
    while n:
        for i in reversed(range(r)):
            cycles[i] -= 1
            if cycles[i] == 0:
                indices[i:] = indices[i+1:] + indices[i:i+1]
                cycles[i] = n - i
            else:
                j = cycles[i]
                indices[i], indices[-j] = indices[-j], indices[i]
                yield tuple(pool[i] for i in indices[:r])
                break
        else:
            return

def convolve(im, ker, cx=1, cy=1, retainVal=None):
    """Super-inefficient convolve. But hey, we can't use numpy.."""
    imout = []

    for y in range(len(im)):
        imout.append([])
        for x in range(len(im[y])):
            response = 0
            for dy in range(len(ker)):#-halfSize, halfSize+1):
                for dx in range(len(ker[dy])):#-halfSize, halfSize+1):
                    if y+dy-cy < 0 or y+dy-cy >= len(im) or x+dx-cx < 0 or x+dx-cx >= len(im[y]):
                        # Out of bounds
                        response += 1 * ker[dy][dx]
                    else:
                        response += im[y + dy - cy][x + dx - cx] * ker[dy][dx]
            if retainVal==None:
                imout[y].append(response)
            else:
                if response == retainVal:
                    imout[y].append(1)
                else:
                    imout[y].append(0)
    return imout

def incrementGrid((x0,y0), (x1,y1), grid, dirgrid, grid_cell_size, countfirst):
    """Copy/edit from Thomas' code.
    Intersects the grid by the line, and increments all visited cells
    """
    dirx = x1 - x0
    diry = y1 - y0
    l = math.sqrt(dirx*dirx + diry*diry)
    dirx /= l
    diry /= l

    grid_cell_size = float(grid_cell_size)
    x0 = x0 / grid_cell_size
    x1 = x1 / grid_cell_size
    y0 = y0 / grid_cell_size
    y1 = y1 / grid_cell_size
    dx = abs(x1 - x0)
    dy = abs(y1 - y0)
    x = int(math.floor(x0))
    y = int(math.floor(y0))
    if dx != 0:
        dt_dx = 1.0 / dx
    else:
        dt_dx = inf
    if dy != 0:
        dt_dy = 1.0 / dy
    else:
        dt_dy = inf
    t = 0.0
    n = 1
    if (dx == 0):
        x_inc = 0
        t_next_horizontal = dt_dx
    elif (x1 > x0):
        x_inc = 1
        n += int(math.floor(x1)) - x
        t_next_horizontal = (math.floor(x0) + 1 - x0) * dt_dx
    else:
        x_inc = -1
        n += x - int(math.floor(x1))
        t_next_horizontal = (x0 - math.floor(x0)) * dt_dx
    if (dy == 0):
        y_inc = 0
        t_next_vertical = dt_dy
    elif (y1 > y0):
        y_inc = 1
        n += int(math.floor(y1)) - y
        t_next_vertical = (math.floor(y0) + 1 - y0) * dt_dy
    else:
        y_inc = -1
        n += y - int(math.floor(y1))
        t_next_vertical = (y0 - math.floor(y0)) * dt_dy

    N = n
    while (n > 0):
        if (n == N and countfirst) or (n < N):
            grid[y][x] += 1
            dirgrid[y][x].append((diry,dirx))

        if (t_next_vertical < t_next_horizontal):
            y += y_inc
            t = t_next_vertical
            t_next_vertical += dt_dy
        else:
            x += x_inc
            t = t_next_horizontal
            t_next_horizontal += dt_dx
        n -= 1
    return

################################################################################
# Module: INTEL
################################################################################
class Edge(object):
    
    def __init__(self, v1, v2):
        self.v1 = v1
        self.v2 = v2
        self.visitedV1 = False
        self.visitedV2 = False
        self.visited = False # True when any of the vertices have been visited

    def visit(self, point):
        """Tell the edge that you've visited one of the vertices.
        point gives the (x,y) coords of the VERTEX you're visiting (must be exact)"""
        if point == self.v1:
            self.visitedV1 = True
            self.visited = True
            return
        if point == self.v2:
            self.visitedV2 = True
            self.visited = True

    def visitedVertex(self, vertex):
        """Return true if the given vertex was visited"""
        if self.visited:
            if self.v1 == vertex and self.visitedV1:
                return True
            elif self.v2 == vertex and self.visitedV2:
                return True
        return False

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            if self.v1 == other.v1 and self.v2 == other.v2:
                return True
            if self.v2 == other.v1 and self.v1 == other.v2:
                return True
        return False

    def __repr__(self):
        return "Edge: {" + str(self.v1) + ", " + str(self.v2) + "}"
        
        
class Graph(object):
    
    def __init__(self, nav_mesh):
        
        self.edgeDict = {} # maps vertices to edge-lists
        self.edgeList = [] # lists all edges
        self.vertList= []  # lists all vertices
        for v1 in nav_mesh.keys():
            if not v1 in self.vertList:
                self.vertList.append(v1)
                
                for v2 in nav_mesh[v1]:
                    e = Edge(v1,v2)
                    if not e in self.edgeList:
                        self.edgeList.append(e)
                        if v1 in self.edgeDict:
                            self.edgeDict[v1].append(e)
                        else:
                            self.edgeDict[v1] = [e]
                        if v2 in self.edgeDict:
                            self.edgeDict[v2].append(e)
                        else:
                            self.edgeDict[v2] = [e]

    def visit(self, point):
        """Marks all vertices within range of point as visited"""
        for v in self.vertList:
            if inRangeManhattan(v, point, g_settings.max_see):
                for e in self.edgeDict[v]:
                    e.visit(v)
    
    def visited(self, vertex):
        """return true if the vertex was visited"""
        return self.edgeDict[vertex][0].visitedVertex(vertex)

    def unvisited(self, maxVerts = -1):
        """Returns a list of unvisted vertices.
        If maxVerts is given, limits results to maxVerts vertices.
        Otherwise all unvisited vertices are returned."""
        unvisited = []
        if maxVerts == -1:
            maxVerts = len(self.vertList)
        else:
            maxVerts = min(maxVerts, len(self.vertList))

        for i in range(maxVerts):
            if not self.visited(self.vertList[i]):
                unvisited.append(self.vertList[i])

        return unvisited

    def __repr__(self):
        return "Graph: {" + str(self.edgeDict) + "}"

class AmmoPack(object):
    """ This class represents a single ammo pack"""
    def __init__(self, x, y, availableIn = 0, inSight = True):
        self.x = x
        self.y = y
        self.availableIn = availableIn # Time until it becomes available. <= 0 means its available
        self.ammoRate = g_settings.ammo_rate # Time between respawns
        self.inSight = inSight  # Whether this pack was observed or observed to be absent in the current time step
        if inSight:
            self.lastInSight = 0   # How many steps ago this pack was last seen (or seen to be absent)
        else:
            self.lastInSight = 1000 # Pretend it was seen once, long ago (symmetric packs are created without really having been spotted)
        self.requests = [None for i in range(6)] # List of agents that want this pack, with request info

    def frameBegin(self):
        self.inSight = False
        #self.availableIn -= 1
        self.lastInSight += 1
        self.requests = [None for i in range(6)]

    def frameEnd(self):
        self.availableIn -= 1
        # If the pack wasn't inSight, and it should be available by now (availableIn < 0), enemy could pick it up..

    def pickedUp(self): # Call when picked up (either by us or opponent)
        self.availableIn = self.ammoRate
    
    def observedPresent(self): # Call when observed to be present
        if self.availableIn > 0:
            self.availableIn = 0
        self.inSight = True
        self.lastInSight = 0 # Last seen now

    def observedAbsent(self): # Call when you can see the position but pack is not there
        #print "Observed to be absent: " + self.__repr__()
        if self.availableIn <= 0: # It should have been present, but someone picked it up..
            self.pickedUp() #self.availableIn = self.ammoRate # Assume it was picked up just now; probably not realistic
        self.inSight = True    
        self.lastInSight = 0 # Last seen now
    
    def requestAmmo(self, agent):
        path = find_path_Astar(agent.observation.loc, (self.x, self.y), agent.mesh, agent.__class__.precalcmesh, agent.grid, g_settings.tilesize)
        length = pathLength(agent.observation.loc, path)
        steps = actualStepsToTarget(agent.observation.loc, agent.observation.angle, path)
        #agent.aprint("respawn: " + str(agent.observation.respawn_in), agent.id) 
        if agent.observation.respawn_in > 0:  # If the agent is still respawning, take into account the waiting time
            steps += agent.observation.respawn_in
        self.requests[agent.id] = [agent, path, length, steps, False]

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.x == other.x and self.y == other.y
        else:
            return False

    def __repr__(self):
        return "AmmoPack: {x: " + str(self.x) + ", y: " + str(self.y) + \
            ", availableIn: " + str(self.availableIn) + ", inSight: " + \
            str(self.inSight) + ", lastInSight: " + str(self.lastInSight) + "}"


def computeCoverSpots(field_grid):
    # standing to the n/e/s/w of a sw/nw/se/ne corner
    global sw_w, sw_s, nw_w, nw_n, se_e, se_s, ne_e, ne_n, coverSpotsMap, coverSpots
    sw_w = convolve( field_grid, [[-1, 1] ,[-1,-1]], 0,0, 1)
    sw_s = convolve( field_grid, [[-1, 1] ,[-1,-1]], 1,1, 1)
    nw_w = convolve( field_grid, [[-1,-1] ,[-1, 1]], 0,1, 1)
    nw_n = convolve( field_grid, [[-1,-1] ,[-1, 1]], 1,0, 1)
    se_e = convolve( field_grid, [[1, -1] ,[-1,-1]], 1,0, 1)
    se_s = convolve( field_grid, [[1, -1] ,[-1,-1]], 0,1, 1)
    ne_e = convolve( field_grid, [[-1,-1] ,[ 1,-1]], 1,1, 1)
    ne_n = convolve( field_grid, [[-1,-1] ,[ 1,-1]], 0,0, 1)

    # Compute a map (like field_grid) containing a 1 if this spot is a cover spot
    # Also construct a list of spots
    coverSpotsMap = []
    coverSpots = []
    for y in range(len(field_grid)):
        coverSpotsMap.append([])
        for x in range(len(field_grid[y])):
            if (sw_w[y][x] or sw_s[y][x] or nw_w[y][x] or nw_n[y][x] or se_e[y][x] or se_s[y][x] or ne_e[y][x] or ne_n[y][x]) and x > len(field_grid[y])/2:
                coverSpotsMap[y].append(1)
                coverSpots.append(((y,x), [], [])) # camspot pos, ..,..
                if sw_w[y][x] or se_e[y][x]:
                    coverSpots[len(coverSpots)-1][2].append((1,0))
                if sw_s[y][x] or nw_n[y][x]:
                    coverSpots[len(coverSpots)-1][2].append((0,-1))
                if nw_w[y][x] or ne_e[y][x]:
                    coverSpots[len(coverSpots)-1][2].append((-1,0))                 
                if se_s[y][x] or ne_n[y][x]:
                    coverSpots[len(coverSpots)-1][2].append((0,1))
            else:
                coverSpotsMap[y].append(0)

    # For each spot, detect from what directions its covered
    dirs = [(1,0), (-1,0), (0,1), (0,-1)]
    for spot in coverSpots:
        for d in dirs:
            if field_grid[spot[0][0] + d[0]][spot[0][1] + d[1]] == 1:
                spot[1].append(d)


################################################################################
# Module: LEARNING
################################################################################
# Ain't gonna work guys ;)
# yeah it is ;)


################################################################################
# Module: AGENTS
################################################################################

count = 0
class AgentBase:
    NAME = "Cyno_Paintrain"
    ammoPacks = []
    ammoRequestIndices = [] # Indices of agents requesting ammo

    totalCampSpots = []

    EnemyCPS = []
    FriendlySpawn = []
    EnemySpawn = []
    graph = None
    
    #used for the input of all agents
    foesTotalList = []
    allyDistanceToAmmo = [[9999 for x in range(6)] for y in range(6)]
    allyDistanceToEnemy = [[9999 for x in range(6)] for y in range(6)]
    allyDistanceToCamp = [[9999 for x in range(3)] for y in range(6)]
    
    campLocsOccupied = [0,0,0]
    
    crowdedness = None
    crowdDir = None
    campSpots = []
    ammoCampSpots = []
    recalculateCampSpots = False
    newAmmoCampSpots = []
    
    precalcmesh = {}
    blob = None

    sortedCPs = []#CPs sorted on x distance


    def __init__(self, id, team, settings=None, field_rects=None, field_grid=None, nav_mesh=None, blob=None):
        """ Each agent is initialized at the beginning of each game.
            The first agent (id==0) can use this to set up global variables.
            Note that the properties pertaining to the game field might not be
            given for each game.
        """
        self.id = id
        self.team = team
        self.mesh = nav_mesh
        self.grid = field_grid
        global g_settings
        g_settings = settings
        self.settings = settings
        self.goal = None




        #NEAT input variables <3
        self.inputVector = [0]*38
        self.distanceToCP = [0]*3
        self.enemyHeldCP = [0]*3
        self.distanceToAmmoLot = [0]*3
        self.spawnTimeMinTTG = [0]*3
        self.closestAllyDistanceAmmo = [0]*3
        self.ammoOnClosestAllyAmmo = [0]*3
        self.ammoFound = [0]
        self.distanceToEnemy = [0]*3
        self.enemyTurn = [0]*3
        self.totalFoesSeen = [0]
        self.distanceToCampSpots = [0]*3
        self.ammoOnCamper = [0]*3
        self.ammoAmount = [0]
        self.averageTeamAmmo = [0]
        self.inCampLocation = [0]
        self.numberAlliesClose = [0]
        self.distanceToEnemySpawn =[0]
        self.campLocAllySteps = [0]*3
        self.enemyAllyWithAmmoSteps = [0]*3
        self.selfTurn = [0]
        
        self.shooting = False
        
        self.sortedAmmo = [] #ammo sorted on distance to current agent        
        self.ammoListIndices  = []
        
        # Bind static variables
        self.ammoPacks = self.__class__.ammoPacks
        self.FriendlySpawn = self.__class__.FriendlySpawn
        self.EnemySpawn = self.__class__.EnemySpawn
        
        self.sortedCPs = self.__class__.sortedCPs #CPs sorted on x distance
        
       
        #self.ammoRequestIndices = self.__class__.ammoRequestIndices
        
        # Recommended way to share variables between agents.
        if id == 0:
            self.all_agents = self.__class__.all_agents = []
        self.all_agents.append(self)
        
        # Scene analysis
        if id == 0:
            # Build map-graph structure:
            self.__class__.graph = Graph(nav_mesh)

            # Determine camp spots
            computeCoverSpots(field_grid)
            
            self.__class__.crowdedness = [[0 for i in field_grid[0]] for j in field_grid]
            self.__class__.crowdDir = [[[] for i in field_grid[0]] for j in field_grid]

        self.graph = self.__class__.graph

        # Create tabular representation of all paths on the navmesh
        if(self.id==0):
            self.__class__.precalcmesh = initAstar(self.mesh, self.grid)
            self.__class__.blob = blob

            

    def aprint(self, string, ID = 0, team = 0):
        """Agent print function. Prints only for the right agents"""
        if self.id == ID and self.team == team:
            pass
            #print "(" + str(self.id) + ", " + str(self.team) + "): " + str(string)
        
    """Special purpose observation functions for each agent.
    May be overwritten in subclass.
    Called from observe()"""
    def agent0Observe(self):  # First call in each frame
        # Any "per-frame reset code" goes here
        for pack in self.ammoPacks:
            pack.frameBegin()
        
        self.__class__.ammoRequestIndices = []
        
        #Sort the Control points on X location
        self.__class__.sortedCPs = sorted(self.observation.cps, key = itemgetter(0))
        if self.team == TEAM_BLUE: #if ours is this team, swap around CPs
            temp = self.__class__.sortedCPs[0]
            self.__class__.sortedCPs[0] = self.__class__.sortedCPs[2]
            self.__class__.sortedCPs[2] = temp
        
        self.__class__.foesTotalList = [] #clear list of total foes
        #print (1,self.__class__.campSpots)
        del self.__class__.totalCampSpots[:]
        self.__class__.totalCampSpots = self.__class__.ammoCampSpots
        for x in self.__class__.campSpots:
            self.__class__.totalCampSpots.append((x[1][0][1]*16+8,x[1][0][0]*16+8))
    def agent1Observe(self):
        pass
    def agent2Observe(self):
        pass    
    def agent3Observe(self):
        pass
    def agent4Observe(self):
        pass
    def agent5Observe(self):
        pass #print self.graph.unvisited()
    
    def validTargetNoFriendlyFire(self, obs, target):
        for x in obs.friends:
            if(line_intersects_circ(obs.loc,target,x[0:2],6)):
                return False #Can shoot, friendly fire!
        return True #we can shoot!
    def validTargetNoEnemyInWay(self, obs, target):
        for x in obs.foes:
            if not(x[0:2] == target[0:2]):
                if(line_intersects_circ(obs.loc,target,x[0:2],6)):
                    return False #enemy target in the way, so we cant shoot
        return True


    def munkresWrapper(self, costmatrix, paddingvalue=sys.maxint):
        n = len(costmatrix)
        m = len(costmatrix[0])

        munk = munkres.Munkres()

        if(n==m):
            indices = munk.compute(costmatrix)
        elif(n<m):
            # there are more targets than agents, pad agents
            padding = [paddingvalue]*m
            costmatrix = costmatrix + [padding]*(m-n)
            indices = munk.compute(costmatrix)
        elif(n>m):
            # there are more agents than targets, pad targets
            for row in costmatrix:
                row = row + [paddingvalue]*(n-m)
                indices = munk.compute(costmatrix)

        return indices


    def tacticalCommander(self):
        
        """
        For the agents that can shoot an enemy, decide which enemies to shoot
        """
        agentsO = []
        agentsAmmo = []
        for i in range(6):
            currentFoes = []
            for x in self.all_agents[i].observation.foes:
                tempbool, assignmentGoal = self.canShoot(i, x)
                if tempbool and not(self.targetIsEnemySpawn(self.all_agents[i].observation, x[0:2])):
                    currentFoes.append(x)
            agentsO.append(currentFoes)
            agentsAmmo.append(self.all_agents[i].observation.ammo)

        attacktable, foes = self.createattacktable(agentsO, agentsAmmo)
        m = munkres.Munkres()

        cost_matrix = m.make_cost_matrix(attacktable, lambda cost: sys.maxint - cost)
        indexes = m.compute(cost_matrix)
        
        for o,a in indexes:
        #    if not foes[a]==-1:
        #        print '(%d, %d) assigned to (%d, %d) with ammo: %d' % (o, a, foes[a][0], foes[a][1], agentsAmmo[o])
            if not foes[a]==-1:
                tempbool, assignmentGoal = self.canShoot(o,foes[a][0:2])
                if tempbool:
                    self.all_agents[o].shooting = True
                    self.all_agents[o].goal = assignmentGoal
                    #print "agent " + str(o) + " attacks " + str(foes[a]) + " with ammo: " + str(agentsAmmo[o])
        



    def createattacktable(self, agentsO, ammo):
        foes = list(set([a for observed in agentsO for a in observed]))
        foes = foes + [-1]*(6-len(foes))

        attacktable = [[0 for i in range(6)] for j in range(6)]
        for o in agentsO:
            for foe in o:
                attacktable[agentsO.index(o)][foes.index(foe)] = ammo[agentsO.index(o)]
        return attacktable, foes



    # Assumption: structure is ordered by ammo descending (done above)
    # Joint shooting uses recursive function to determine
    # an assignment to shoot opponents

    def canShoot(self, o, a , assignmentGoal = None):
        if(self.all_agents[o].observation.ammo > 0):
            thisLoc = self.all_agents[o].observation.loc
            thisAngle = self.all_agents[o].observation.angle
            targetLoc = a[0:2]
            dx = targetLoc[0]-thisLoc[0] #remove this after Robrechts code integration
            dy = targetLoc[1]-thisLoc[1]
            #turn = angle_fix(math.atan2(dy, dx)-thisAngle)
            
            #print ("o:",o,"a:",a,point_dist(targetLoc, thisLoc) < self.settings.max_range,self.validTargetNoFriendlyFire(self.all_agents[o].observation, targetLoc), turn < self.settings.max_turn and turn > -self.settings.max_turn, not line_intersects_grid(thisLoc, targetLoc, self.grid, self.settings.tilesize))
            if (#we can shoot an enemy middle
                self.canShootValidityCheck(o,thisAngle,targetLoc,thisLoc)):
                return True, targetLoc
            #calculate orthogonal points on the outer part of the hitbox
            orthx = -dy
            orthy = dx
            lengthvector = math.sqrt(orthx**2 + orthy**2)
            orthx = (orthx*5.99)/lengthvector
            orthy = (orthy*5.99)/lengthvector
            orthpointx = orthx + targetLoc[0]
            orthpointy = orthy + targetLoc[1]
            #print(targetLoc , (orthpointx,orthpointy))
            if (#we can shoot an enemy middle
                self.canShootValidityCheck(o,thisAngle,(orthpointx,orthpointy),thisLoc)):
                return True, (orthpointx,orthpointy)
            
            orthx = dy
            orthy = -dx
            #lengthvector = math.sqrt(orthx**2 + orthy**2)
            orthx = (orthx*5.99)/lengthvector
            orthy = (orthy*5.99)/lengthvector
            orthpointx = orthx + targetLoc[0]
            orthpointy = orthy + targetLoc[1]
            #print(targetLoc , (orthpointx,orthpointy))
            if (#we can shoot an enemy middle
                self.canShootValidityCheck(o,thisAngle,(orthpointx,orthpointy),thisLoc)):
                return True, (orthpointx,orthpointy)
            
        return False, (0,0)
    def canShootValidityCheck(self,o,thisAngle,targetLoc,thisLoc):
        dx = targetLoc[0]-thisLoc[0] #remove this after Robrechts code integration
        dy = targetLoc[1]-thisLoc[1]
        turn = angle_fix(math.atan2(dy, dx)-thisAngle)
        
        value = point_dist(targetLoc, thisLoc) < self.settings.max_range + 5.9 and self.validTargetNoFriendlyFire(self.all_agents[o].observation, targetLoc) and self.validTargetNoEnemyInWay(self.all_agents[o].observation, targetLoc) and turn < self.settings.max_turn and turn > -self.settings.max_turn and not line_intersects_grid(thisLoc, targetLoc, self.grid, self.settings.tilesize) #and no wall
        
        return value
                
    def sortOnArray(self, array, sort):
        struct = [(i,array[i], sort[i]) for i in range(len(sort))]
        return sorted(struct, key=lambda ammo: ammo[2], reverse=True)
    

    def incrementCrowdedness(self, source, target):
        self.__class__.recalculateCampSpots = True # Campspots must be recalculated
        path = find_path_Astar(source, target, self.mesh, self.__class__.precalcmesh, self.grid, g_settings.tilesize)
        path.reverse()
        path.append(source)
        path.reverse()

        # Increment the squares on each edge in the path
        for i in range(1, len(path)):
            incrementGrid( path[i-1], path[i], self.__class__.crowdedness, self.__class__.crowdDir, g_settings.tilesize, i == 1)

        #for row in self.__class__.crowdedness:
        #    print row

        #for row in self.__class__.crowdDir:
        #    print row
        #
        
    def calculateCampSpots(self):
        offsets = [(-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1)]
        self.__class__.campSpots = []

        for spot in coverSpots:
            #print "CHECKING SPOT: ", spot

            value = 0
            spotSafe = True
            

            for offset in spot[2]:#offsets:
                #print "-offset: " + str(offset)
                value = max(value, self.__class__.crowdedness[spot[0][0]+offset[0]][spot[0][1]+offset[1]])
                #print "--value: " + str(value)
                for crowdDir in self.__class__.crowdDir[spot[0][0]+offset[0]][spot[0][1]+offset[1]]:
                    #print "---crowdDir: " + str(crowdDir)
                    dirSafe = False
                    for coverDir in spot[1]:
                        #print "----coverDir: " + str(coverDir)
                        #print "----dot: " + str(crowdDir[0]*coverDir[0] + crowdDir[1]*coverDir[1] <= -math.sqrt(2))
                        if crowdDir[0]*coverDir[0] + crowdDir[1]*coverDir[1] <= math.cos( 135 * math.pi / 180):#-math.sqrt(2)/2:
                            dirSafe = True
                        #print "----dirSafe? " + str( dirSafe )

                    if dirSafe == False:
                        spotSafe = False
                        break
                if spotSafe == False:
                    break

            if spotSafe and value > 2:
                self.__class__.campSpots.append((value, spot))
                #assert(False)

        #print self.__class__.campSpots

                
        
    def observe(self, observation):
        """ Each agent is passed an observation using this function,
            before being asked for an action. You can store either
            the observation object or its properties to use them
            to determine your action. Note that the observation object
            is modified in place.
        """
        self.previousGoal = self.goal #set the previous goal
                    
        self.observation = observation
        self.selected = observation.selected

        # Mark my new location as visited:
        self.graph.visit(observation.loc)
        
        # Execute per-ID observation code (these may be overwritten in subclass)
        if(self.id == 0):
            self.agent0Observe()
        elif(self.id == 1):
            self.agent1Observe()
        elif(self.id == 2):
            self.agent2Observe()
        elif(self.id == 3):
            self.agent3Observe()
        elif(self.id == 4):
            self.agent4Observe()
        elif(self.id == 5):
            self.agent5Observe()
        
        self.shooting = False #don shoot unless told so!
        
        #on the first step, for each agent, determine where the self and opponent spawn is
        if(observation.step == 1):
            self.FriendlySpawn.append(observation.loc)
            self.EnemySpawn.append((656-observation.loc[0],observation.loc[1]))
            #print "EnemySpawn: " + str(self.EnemySpawn)
            
            # Add CPs to crowdedness map
            if (self.id == 1):
                self.incrementCrowdedness(self.EnemySpawn[0], (observation.cps[0][0], observation.cps[0][1]))
            elif self.id == 2:
                self.incrementCrowdedness(self.EnemySpawn[0], (observation.cps[1][0], observation.cps[1][1]))
            elif self.id == 3:
                self.incrementCrowdedness(self.EnemySpawn[0], (observation.cps[2][0], observation.cps[2][1]))
                # Calculate good camp spots
                #self.calculateCampSpots()
        

        #agent 0 determines the enemy control points
        if(self.id == 0): 
            del self.EnemyCPS[:]
            for x in range(len(observation.cps)): 
                if observation.cps[x][2] != self.team:
                    self.EnemyCPS.append(observation.cps[x])         
        
        #All agents add their visible foes to a list
        for x in self.observation.foes:
            if x not in self.__class__.foesTotalList: #make the list unique
                self.__class__.foesTotalList.append(x)
                
        packObs = filter(lambda x: x[2] == "Ammo", observation.objects)
        self.processAmmoObservations(packObs, observation.loc)
        
        
        #Calculate for each agent their input function parts based on their own observation
        self.calculateOwnInputObs()

        #agent 5 calculates for every tank the path distance to all the foes
        
        if self.id == 5:
            self.calcAllAlliesDistFoes() #uses foesTotalList, hence has to be done by 5
            
        if self.id == 5:
            self.tacticalCommander()
    def calcAllAlliesDistFoes(self):
        self.__class__.allyDistanceToEnemy = [[9999 for x in range(6)] for y in range(6)]
        for i in range(6):
            for j in range(len(self.__class__.foesTotalList)):
                path = find_path_Astar(self.all_agents[i].observation.loc, self.__class__.foesTotalList[j][0:2], self.mesh, self.__class__.precalcmesh, self.grid, self.settings.tilesize)
                distance = actualStepsToTarget(self.all_agents[i].observation.loc, self.all_agents[i].observation.angle, path)
                #pathLength(self.all_agents[i].observation.loc, path) #get the distance to each enemy j from agent i
                self.__class__.allyDistanceToEnemy[i][j] = distance
        
    def fill_distanceToCP(self):
        """Distances to all control points. assuming sorted CP list
        """
        distances = [0,0,0] #store all distances
        for i in range(3): #loop over all CPs, first one is the one close to selfspawn
            distances[i] = actualStepsToTarget(self.observation.loc, self.observation.angle, find_path_Astar(self.observation.loc, self.__class__.sortedCPs[i][0:2], self.mesh, self.__class__.precalcmesh, self.grid, self.settings.tilesize))
            #pathLength(self.observation.loc, find_path_Astar(self.observation.loc, self.__class__.sortedCPs[i][0:2], self.mesh, self.__class__.precalcmesh, self.grid, self.settings.tilesize))
        return distances
    def fill_enemyHeldCP(self):
        """ Check on who controls the control points
        0 if it's yourself, 1 otherwise (neutral is classified as opponents)
        """
        whoHolds = [0,0,0]
        for i in range(3):
            if(not(self.__class__.sortedCPs[i][2] == self.team)): #gets the 3rd value in the sortedCPs
                whoHolds[i] = 1 #if this is not our team, its an enemies (or neutral )
        return whoHolds
    def zeroOrHigher(self, a):
        if a < 0:
            return 0
        else:
            return a
    def fill_distanceToAmmoLot(self):
        """calculate distances to closest 3 ammo spots. also fills self.sortedAmmo
        which contains the actual locations of these 3 closest ones
        """
        ammoList = []
        turnsTuple = []
        timeToSpawn = []
        for i in range(len(self.ammoPacks)):
            path = find_path_Astar(self.observation.loc, (self.ammoPacks[i].x, self.ammoPacks[i].y), self.mesh, self.__class__.precalcmesh, self.grid, self.settings.tilesize)
            distance = actualStepsToTarget(self.observation.loc, self.observation.angle, path)
            #pathLength(self.observation.loc, path) #get the distance to each ammoPack
            ammoList.append((distance, self.ammoPacks[i].x, self.ammoPacks[i].y, actualStepsToTarget(self.observation.loc, self.observation.angle, path),self.ammoPacks[i].availableIn, i))
            self.__class__.allyDistanceToAmmo[self.id][i] = distance
        ammoList = sorted(ammoList, key = itemgetter(0)) #sort the ammoList on the distance
        self.ammoListIndices = [0,0,0,0,0,0]
        for i in range(len(self.ammoPacks)): #keep the indices of what we sorted
            self.ammoListIndices[i] = ammoList[i][5]
        if len(self.ammoPacks) >= 3: #more than three ammo spots available
            self.sortedAmmo = [(ammoList[0][1],ammoList[0][2]),(ammoList[1][1],ammoList[1][2]),(ammoList[2][1],ammoList[2][2])] #closest three ammo sorted on distance to current agent
            #calculate the tuple of turns from distances
            turnsTuple = [ammoList[0][3],ammoList[1][3],ammoList[2][3]]
            timeToSpawn = [ammoList[0][4],ammoList[1][4],ammoList[2][4]]
        elif (len(self.ammoPacks) == 2): #only 2 ammo spots available, otherwise ignore assignment
            self.sortedAmmo = [(ammoList[0][1],ammoList[0][2]),(ammoList[0][1],ammoList[0][2]),(ammoList[1][1],ammoList[1][2])] #closest three ammo sorted on distance to current agent
            turnsTuple = [ammoList[0][3],ammoList[0][3],ammoList[1][3]]
            timeToSpawn = [ammoList[0][4],ammoList[0][4],ammoList[1][4]]
        else:
            self.sortedAmmo = []
            turnsTuple = [30,30,30]
            timeToSpawn = [100,100,100]

        turnsMinSpawnTuple = [self.zeroOrHigher(timeToSpawn[0] - turnsTuple[0]),self.zeroOrHigher(timeToSpawn[1] - turnsTuple[1]),self.zeroOrHigher(timeToSpawn[2] - turnsTuple[2])]
        
        
        
        #return the distances
        if len(self.ammoPacks) >= 3:
            return [ammoList[0][0],ammoList[1][0],ammoList[2][0]] ,turnsMinSpawnTuple
        elif len(self.ammoPacks) == 2: #first two are the first pack if only 2 are found
            return [ammoList[0][0],ammoList[0][0],ammoList[1][0]] ,turnsMinSpawnTuple
        else: 
            return [9999,9999,9999], turnsMinSpawnTuple # no packs found :(, distance on 9999 (far away)
    def fill_inCampLocation(self):
        """
        Checks if the current agent is in one of the defined camp locations
        totalCampSpots, returns 1 if true, 0 otherwise
        """
        if self.id == 0:
            for i in range(3):
                self.__class__.campLocsOccupied[i] = 0
        booleanVal = 0 #actually returned if we are in a camp spot
        for i in range(3): 
            if i < len(self.__class__.totalCampSpots):
                if math.sqrt((self.__class__.totalCampSpots[i][0] - self.observation.loc[0])**2 + (self.__class__.totalCampSpots[i][1] - self.observation.loc[1])**2) < 2:
                    booleanVal = 1
                    self.__class__.campLocsOccupied[i] = self.observation.ammo
            else:
                pass
        return [booleanVal]
    def fill_distanceToCampSpots(self):
        """
        Fill in path distance to each camp spot
        """
        distances = [0,0,0]
        for i in range(3):
            if i < len(self.__class__.totalCampSpots):
                distances[i] = actualStepsToTarget(self.observation.loc, self.observation.angle, find_path_Astar(self.observation.loc, self.__class__.totalCampSpots[i][0:2], self.mesh, self.__class__.precalcmesh, self.grid, self.settings.tilesize))
                #pathLength(self.observation.loc, find_path_Astar(self.observation.loc, self.__class__.totalCampSpots[i][0:2], self.mesh, self.__class__.precalcmesh, self.grid, self.settings.tilesize))
                self.__class__.allyDistanceToCamp[self.id][i] = distances[i]
            else:
                distances[i] = 9999
        return distances
    def fill_distanceToEnemySpawn(self):
        """
        Calculate distance of this agent to the enemy spawn
        """
        return [actualStepsToTarget(self.observation.loc, self.observation.angle, find_path_Astar(self.observation.loc, (self.__class__.EnemySpawn[0][0],self.__class__.EnemySpawn[0][1]), self.mesh, self.__class__.precalcmesh, self.grid, self.settings.tilesize))]
    def calculateOwnInputObs(self):
        """calls all functions needed to calculate all seperate parts that
        comprise the input function for NEAT
        """
        self.distanceToCP = self.fill_distanceToCP()
        self.enemyHeldCP = self.fill_enemyHeldCP()
        self.distanceToAmmoLot, self.spawnTimeMinTTG = self.fill_distanceToAmmoLot() #also fills in the closestAmmo list, so call this before the other ammo ones. Also calculates the spawnTimeMinTTG, as it uses the exact same information
        self.closestAllyDistanceAmmo = [0]*3 #fill in at the beginning of action
        self.ammoOnClosestAllyAmmo = [0]*3   #fill in at the beginning of action
        self.ammoFound = [len(self.__class__.ammoPacks)]
        self.distanceToEnemy = [0]*3         #fill in at the beginning of action
        self.enemyTurn = [0]*3            #fill in at the beginning of action
        self.totalFoesSeen = [0] #fill in at the beginning of action
        self.distanceToCampSpots = self.fill_distanceToCampSpots()  
        self.ammoOnCamper = [0]*3            #fill in at the beginning of action
        self.ammoAmount = [self.observation.ammo]
        self.averageTeamAmmo = [0]           #fill in at the beginning of action
        self.inCampLocation = self.fill_inCampLocation()
        self.numberAlliesClose = [len(self.observation.friends)]
        self.distanceToEnemySpawn = self.fill_distanceToEnemySpawn()
        self.campLocAllySteps = [0]*3 #fill in at the beginning of action
        self.enemyAllyWithAmmoSteps = [0]*3 #fill in at the beginning of action
        self.selfTurn = [self.observation.angle]

        
    def processAmmoObservations(self, packObs, loc):
        """Takes the ammo observations of an agent as well as the agents location,
           checks if it contains ammo observations not registered before,
           and adds those to the shared knowledge of ammo packs.
           Also signals the presence or absense of all known packs in range."""
        # Function can surely be made much more efficient.

        # Get a list of ammopacks that we should be able to see
        observables = filter(lambda pack: inRangeManhattan((pack.x,pack.y),loc,g_settings.max_see),
                             self.__class__.ammoPacks)
        
        # Signal observations of known packs, or add pack if unknown
        for ob in packObs:
            seenBefore = False
            for pack in self.__class__.ammoPacks: #observables: #for pack in type(self).ammoPacks:
                if pack.x == ob[0] and pack.y == ob[1]:
                    pack.observedPresent()
                    seenBefore = True
                    break
            if not seenBefore:
                self.__class__.ammoPacks.append(AmmoPack(ob[0],ob[1],self.settings.ammo_rate))
                self.__class__.ammoPacks.append(AmmoPack(656 - ob[0],ob[1],self.settings.ammo_rate))
                assert(len(self.__class__.ammoPacks) <= 6)
                #self.aprint("New ammo pack discovered ", self.id)
                # Add requests to new packs for all previous agents requiring ammo 
                for agentId in self.__class__.ammoRequestIndices:
                    self.aprint("Adding request: " + str(len(self.ammoPacks) - 1) + ", " + str(agentId), self.id)
                    self.ammoPacks[len(self.ammoPacks) - 1].requestAmmo(self.all_agents[agentId])
                    self.ammoPacks[len(self.ammoPacks) - 2].requestAmmo(self.all_agents[agentId])
                
                # Update crowdedness map
                self.incrementCrowdedness(self.EnemySpawn[0], (ob[0], ob[1]))
                self.incrementCrowdedness(self.EnemySpawn[0], (656 - ob[0], ob[1]))

                # Calculate good camp spots
                #self.calculateCampSpots()
                self.__class__.recalculateCampSpots = True

                #print "ADDING AMMO SPOT"
                # See if these packs are good to camp
                #if( not line_intersects_grid(self.EnemySpawn[0], (ob[0], ob[1]), self.grid, 16)):
                #    d = math.sqrt((self.EnemySpawn[0][0]-ob[0])**2 + (self.EnemySpawn[0][1]-ob[1])**2)
                #    if d >= 0 and d <= 128:
                #        self.__class__.ammoCampSpots.append((ob[0], ob[1]))
                #print "Intersect: " + str(656-ob[0]) + " " + str(ob[1]) + " " + str(self.EnemySpawn[0])
                if( not line_intersects_grid(self.EnemySpawn[0], (656-ob[0], ob[1]), self.grid, 16)):
                    d = math.sqrt((self.EnemySpawn[0][0]-656+ob[0])**2 + (self.EnemySpawn[0][1]-ob[1])**2)
                    #print "DISTANCE: " + str(d)
                    if d <= 256:
                        #self.__class__.ammoCampSpots.append((656-ob[0], ob[1]))
                        self.__class__.newAmmoCampSpots.append((656-ob[0], ob[1]))
                        #print "ADDING:" + str((656-ob[0], ob[1]))

        # Signal absence of pack
        for pack in observables:
            wasObservedPresent = False
            for ob in packObs:
                if pack.x == ob[0] and pack.y == ob[1]:
                    wasObservedPresent = True
            if not wasObservedPresent:
                pack.observedAbsent()
                       


    """Special purpose action functions for each agent.
    May be overwritten in subclass.
    Called from action()"""
    def agent0Action(self):
        for pack in self.ammoPacks:
            pack.frameEnd()
        
    def agent1Action(self):
        pass
    def agent2Action(self):
        pass
    def agent3Action(self):
        pass
    def agent4Action(self):
        if self.__class__.recalculateCampSpots:
            self.__class__.recalculateCampSpots = False
            self.calculateCampSpots()
        if self.__class__.newAmmoCampSpots:
            self.__class__.ammoCampSpots.extend(self.__class__.newAmmoCampSpots)
            self.__class__.newAmmoCampSpots = []
    def agent5Action(self):
        pass

    def targetIsEnemySpawn(self,obs,target):
        #spawn = filter(lambda x: x[0] == target[0] and x[1] == target[1], self.EnemySpawn)
        for x in self.EnemySpawn:
            if line_intersects_circ(obs.loc, target, x, 9.0):
                return True
    
    def naiveMoveTo(self,obs):
        path = find_path_Astar(obs.loc, self.goal, self.mesh, self.__class__.precalcmesh, self.grid, self.settings.tilesize)
        #if self.id==0:
            #print "Newpath " + str(path)
            #print "Oldpath " + str(find_path(obs.loc, self.goal, self.mesh, self.grid, self.settings.tilesize))
        if path:
            dx = path[0][0]-obs.loc[0]
            dy = path[0][1]-obs.loc[1]
            turn = angle_fix(math.atan2(dy, dx)-obs.angle)
            if(turn < math.pi/2 and turn > -math.pi/2): #if movement target is in front of the agent
                speed = (dx**2 + dy**2)**0.5
            else: #target in the back, turn around first
                speed = 1
        else:
            turn = 0
            speed = 0
        return (turn,speed)
    def actionMoveToCP(self, number):
        """
        Returns the turn/speed/shoot triplet for the action that moves an agent
        to a control point. The control point is CP point with 'number' as ID,
        meaning 0 for the campspot closest to the self-spawn, and 2 closest to
        the enemy spawn
        """
        self.goal = self.__class__.sortedCPs[number][0:2] #set as goal
        
        #if we are far, move to goal
        if math.sqrt((self.observation.loc[0]-self.goal[0])**2 + (self.observation.loc[1]-self.goal[1])**2 ) > 6:
            (turn, speed) = self.naiveMoveTo(self.observation)
        else: #if we are on the goal, just turn to opponents spawn
            self.goal = self.EnemySpawn[0][0:2]
            (turn, speed) = self.naiveMoveTo(self.observation)
            speed = 0 #but dont move towards it!
        return (turn, speed, False)
    def actionMoveToAmmo(self, number):
        """
        Moves the action to the number'th ammo spot that is defined
        Now with sortedAmmo (first 3 closest ones)
        """
        if number >= len(self.sortedAmmo): #if we haven't found enough
            return self.actionExploration() #ammo spots, we explore for more!
    
        self.goal = (self.sortedAmmo[number][0],self.sortedAmmo[number][1]) #set as goal
        #if we are far, move to goal
        if math.sqrt((self.observation.loc[0]-self.goal[0])**2 + (self.observation.loc[1]-self.goal[1])**2 ) > 6:
            (turn, speed) = self.naiveMoveTo(self.observation)
        else: #if we are on the goal, just turn to opponents spawn
            self.goal = self.EnemySpawn[0][0:2]
            (turn, speed) = self.naiveMoveTo(self.observation)
            speed = 0 #but dont move towards it!
        return (turn, speed, False)
    def actionExploration(self):
        """
        Function that makes the agent walk to unfound vertices to look for ammo
        spawns. Picks the unvisited node that is closest by to explore. If all
        nodes are explored, it simply moves to the CP closest to the enemy
        """
        unvisitedVertices = self.graph.unvisited() #get the vertices that were no seen
            
        if(unvisitedVertices):  #if they exist
            #find the closest
            closestVerticeDist = 99999999
            closestVertice = (0,0)
            for x in unvisitedVertices:
                dist = (self.observation.loc[0] - x[0])**2 +(self.observation.loc[1] - x[1])**2
                if dist < closestVerticeDist:
                    closestVertice = x
                    closestVerticeDist = dist
                pass
            self.goal = closestVertice
            (turn, speed) = self.naiveMoveTo(self.observation)
        else: #if no unexplored vertices, move to the last CP I guess
            return self.actionMoveToCP(2)
        return (turn, speed, False)
    def actionSpawncamp(self):
        """
        Function defines the spawncamping behavior of agents. The agents move
        towards the enemy spawn and stand 60 units outside of its range, aiming
        at the middle.
        """
        self.goal = self.EnemySpawn[0][0:2] #move to enemy spawn
        (turn, speed) = self.naiveMoveTo(self.observation)
        if math.sqrt((self.observation.loc[0]-self.goal[0])**2 + (self.observation.loc[1]-self.goal[1])**2) < 100: #if we are within 100 units, restrict the speed
            speed = math.sqrt((self.observation.loc[0]-self.goal[0])**2 + (self.observation.loc[1]-self.goal[1])**2)-60 #move till 60 units
        return (turn, speed, False)
    def actionAttackEnemy(self, number):
        """
        Function makes the agent chase and target the number'th agent, as defined
        no in the total, and later in the sorted list of agents
        """
        if number >= len(self.__class__.foesTotalList): #not a valid number... do something else
            return self.actionSpawncamp() #go spawncamp ^_^
        else:
            self.goal = self.__class__.foesTotalList[number][0:2]
            (turn, speed) = self.naiveMoveTo(self.observation)
        return (turn, speed, False)
    def actionRotateToEnemy(self, number):
        """
        Function makes the agent rotate towards a certain enemy, useful for
        when spawncamping for example
        """
        if number >= len(self.__class__.foesTotalList): #not a valid number... do something else
            (turn, speed, shoot) = self.actionSpawncamp()
            return (turn, 0, False) #go rotate towards enemy
        else:
            self.goal = self.__class__.foesTotalList[number][0:2]
            (turn, speed) = self.naiveMoveTo(self.observation) # move to foe
        speed = 0 #but set speed to 0 so we only rotate
        return (turn, speed, False)
    def actionMoveToCamp(self, number):
        """
        This function makes the agent move towards a target camping spot, and
        remain there, pointing towards the enemy spawn's path. Currently 
        first filling up ammo spawn locs, then the rest
        """
        #print self.__class__.totalCampSpots
        if len(self.__class__.totalCampSpots) <= number: #not enough spots to fill!
            return self.actionSpawncamp()
        else:
            self.goal = self.__class__.totalCampSpots[number]
            
                    #if we are far, move to goal
            if math.sqrt((self.observation.loc[0]-self.goal[0])**2 + (self.observation.loc[1]-self.goal[1])**2 ) > 2:
                (turn, speed) = self.naiveMoveTo(self.observation)
            else: #if we are on the goal, just turn to opponents spawn
                self.goal = self.EnemySpawn[0][0:2]
                (turn, speed) = self.naiveMoveTo(self.observation)
                speed = 0 #but dont move towards it!
        return (turn, speed, False)
    def fill_averageTeamAmmo(self):
        """
        Average ammo of the team
        """
        averageAmmo = 0
        for i in range(6):
            averageAmmo = averageAmmo + self.all_agents[i].observation.ammo
        return [averageAmmo/6.]
    def fill_totalFoesSeen(self):
        """
        Returns the total number of foes that all agents can see
        """
        return [len(self.__class__.foesTotalList)]
    def fill_ammoOnCamper(self):
        """
        For each of the three camp locations, list the amount of ammo that the agent on
        it has, otherwise if there's no agent, list 0
        """
        return copy.copy(self.__class__.campLocsOccupied)
    def fill_closestAllyDistanceAmmo(self):
        """
        Fills minimumDistances, the distances of each of the three close ammo locations
        to the closest other agent. And minAgentAmmout, the amount of ammo that agent has
        """
        minimumDistances = [0,0,0]
        minAgentAmmoAmount = [0,0,0]
        for i in range(3): #for each ammo pack in sortedAmmo
            #look up which index it has in ammoPacks
            if i < len(self.sortedAmmo):
                self.sortedAmmo[i]
                j = self.ammoListIndices[i] #<- it's actually this ammo pack
                mindist = 9999
                minagentnum = 0
                for k in range(6):#to this ammopack, which agent has the closest range? (not including ourself)
                    if self.__class__.allyDistanceToAmmo[k][j] < mindist and not k== self.id:
                        mindist = self.__class__.allyDistanceToAmmo[k][j]
                        minagentnum = k
                #now k is the num of the agent with the least distance to ammo spot j
                #and mindist is the minimum distance
                minimumDistances[i] = mindist
                minAgentAmmoAmount[i] = self.all_agents[minagentnum].observation.ammo
            else:
                minimumDistances[i] = 9999
                minAgentAmmoAmount[i] = 20
        return minimumDistances, minAgentAmmoAmount
    def fill_distanceToEnemy(self):
        """
        Simultaneously fills self.distanceToEnemy, self.enemyTurn, self.enemyAllyWithAmmoSteps
        self.distanceToEnemy is simply the distance to each closest enemy from this agent
        self.enemyTurn is the turn of those enemies
        self.enemyAllyWithAmmoSteps gives the distance of the closest ally to the target
        that also has ammo
        """
        shortedThreeDists = [9999,9999,9999] #init on 9999
        enemyTurns = [9999,9999,9999]
        closestAmmoAllyDist = [9999,9999,9999]
        sortedEnemies = sorted(zip(self.__class__.allyDistanceToEnemy[self.id],range(6)), key = itemgetter(0)) #sorted list of the enemies, sorted on distance
        for i in range(3): #also adds index to the sorting to know which target enemy it is
            shortedThreeDists[i] = sortedEnemies[i][0] #get the value of the shortest distances
            enemy_index = sortedEnemies[i][1] #the zipped list is the index before sorting
            if enemy_index < len(self.__class__.foesTotalList):
                enemyTurns[i] =  angle_fix(math.atan2(self.observation.loc[0] - self.__class__.foesTotalList[enemy_index][0], self.observation.loc[1] - self.__class__.foesTotalList[enemy_index][1])-self.observation.angle)
                #self.__class__.foesTotalList[enemy_index][2]
                #Now we calculate the the steps it takes for the ally with ammo who is the closest to
                #the targets
                mindistance = 9999
                for j in range(6):
                       if (not (j == i)) and self.all_agents[j].observation.ammo > 0:
                        #this agent j has ammo and is not myself, so we check its mindistance to the target enemy
                        if self.__class__.allyDistanceToEnemy[j][enemy_index] < mindistance:
                            mindistance = self.__class__.allyDistanceToEnemy[j][enemy_index] #smaller mindistance!
                closestAmmoAllyDist[i] = mindistance #this is the minimum distance that any other agent with ammo has to the target
        return shortedThreeDists, enemyTurns, closestAmmoAllyDist
    def fill_campLocAllySteps(self):
        """
        fills self.campLocAllySteps, the distance of the closest agent that has 
        ammo and that is not this agent, to the defined campspots.
        """
        mindistances = [9999,9999,9999] #stores the minimal distances to return
        for i in range(3): #for each target campspot
            for j in range(6): #for each agent
                if (not (i == j)) and self.all_agents[j].observation.ammo > 0: #for each agent j that is not the current agent, and that has ammo
                    if self.__class__.allyDistanceToCamp[j][i] < mindistances[i]:
                        mindistances[i] = self.__class__.allyDistanceToCamp[j][i] #if its distance is lower, change the mindistances
        return mindistances
                
    def finalInputProcess(self):
        """
        Sets all input variables, also based on the observations of all other agents.
        Used for the NEAT input.
        """
        #self.distanceToCP
        #self.enemyHeldCP
        #self.distanceToAmmoLot, self.spawnTimeMinTTG
        self.closestAllyDistanceAmmo, self.ammoOnClosestAllyAmmo = self.fill_closestAllyDistanceAmmo() #fill in at the beginning of action
        #self.ammoFound
        self.distanceToEnemy, self.enemyTurn, self.enemyAllyWithAmmoSteps = self.fill_distanceToEnemy()        #fill in at the beginning of action
        self.totalFoesSeen = self.fill_totalFoesSeen() #can be done by 1 agent and static
        #self.distanceToCampSpots
        self.ammoOnCamper = self.fill_ammoOnCamper()        
        #self.ammoAmount
        self.averageTeamAmmo = self.fill_averageTeamAmmo()  #can be done by 1 agent and static
        #self.inCampLocation
        #self.numberAlliesClose
        #self.distanceToEnemySpawn
        self.campLocAllySteps = self.fill_campLocAllySteps() #fill in at the beginning of action
    def actionShooting(self):
        (turn, speed) = self.naiveMoveTo(self.observation)
        shoot = True
        return (turn, speed, shoot)
    def action(self):
        """ This function is called every step and should
            return a tuple in the form: (turn, speed, shoot)
        """
        if not self.shooting: #is we shoot, we do not have to process the input thingies
            self.finalInputProcess() #calculates the last obseved items for each agent, based on the observations of all agents
            
            
        if(self.id == 0):
            self.agent0Action()
        elif(self.id == 1):
            self.agent1Action()
        elif(self.id == 2):
            self.agent2Action()
        elif(self.id == 3):
            self.agent3Action()
        elif(self.id == 4):
            self.agent4Action()
        elif(self.id == 5):
            self.agent5Action()
            
        if not self.shooting: #if we shoot, do not process the neural network stuff
            #calculate the full input vector
                    #NEAT input variables <3
            self.inputVector = self.distanceToCP + self.enemyHeldCP + self.distanceToAmmoLot + self.spawnTimeMinTTG + self.closestAllyDistanceAmmo + self.ammoOnClosestAllyAmmo + self.ammoFound + self.distanceToEnemy + self.enemyTurn + self.totalFoesSeen + self.distanceToCampSpots + self.ammoOnCamper + self.ammoAmount  + self.averageTeamAmmo + self.inCampLocation + self.numberAlliesClose + self.distanceToEnemySpawn + self.campLocAllySteps + self.enemyAllyWithAmmoSteps + self.selfTurn
            
            #print self.inputVector
            
            #ROBRECHT <- Code van de actie selectie gaat hier, met als input: self.inputVector
    
            # Cast to float
            self.inputVector = [float(x) for x in self.inputVector]
    
            # Set as input to network
            output = self.__class__.blob.sactivate(self.inputVector)
    
            action = output.index(max(output))
    
    
     
            #print len(self.inputVector) = 44!
            
            #action = 0# <- let the algoritm modify this value
            
            #The actions we take, in a specific order!
            if action == 0: 
                (turn,speed,shoot) = self.actionMoveToCP(0)
            elif action == 1: 
                (turn,speed,shoot) = self.actionMoveToCP(1)
            elif action == 2:
                (turn,speed,shoot) = self.actionMoveToCP(2)
            elif action == 3:
                (turn,speed,shoot) = self.actionMoveToAmmo(0)
            elif action == 4: 
                (turn,speed,shoot) = self.actionMoveToAmmo(1)
            elif action == 5:
                (turn,speed,shoot) = self.actionMoveToAmmo(2)
            elif action == 6:
                (turn,speed,shoot) = self.actionExploration()
            elif action == 7:
                (turn,speed,shoot) = self.actionAttackEnemy(0)
            elif action == 8:
                (turn,speed,shoot) = self.actionAttackEnemy(1)
            elif action == 9:
                (turn,speed,shoot) = self.actionAttackEnemy(2)
            elif action == 10:
                (turn,speed,shoot) = self.actionRotateToEnemy(0)
            elif action == 11:
                (turn,speed,shoot) = self.actionRotateToEnemy(1)
            elif action == 12:
                (turn,speed,shoot) = self.actionRotateToEnemy(2)
            elif action == 13:
                (turn,speed,shoot) = self.actionSpawncamp()
            elif action == 14: 
                (turn,speed,shoot) = self.actionMoveToCamp(0)
            elif action == 15: 
                (turn,speed,shoot) = self.actionMoveToCamp(1)
            elif action == 16:
                (turn,speed,shoot) = self.actionMoveToCamp(2)
        else:
            (turn, speed, shoot) = self.actionShooting()
        
      
        #comment out the following code if you are going to test the output,
        #Im using this to test the output and input variables, no toucha!
        #if(self.id == 5 and self.team == 0):  
        #   print self.goal, (turn,speed,shoot)
        #if not self.shooting:
        #        turn = 0
        #        speed = 0
        #        shoot = 0
        #if self.id == 0 or self.id == 3:
        #    if not self.shooting:
        #        turn = 0
        #        speed = 0
        #        shoot = 0
        #        if self.observation.step < 20:
        #            (turn, speed, shoot ) = self.actionExploration()
        #        elif self.observation.step < 50:
        #            (turn, speed, shoot ) = self.actionMoveToAmmo(0)
        #        else:
        #            (turn, speed, shoot ) = self.actionSpawncamp()
        return (turn,speed,shoot)
        
    def debug(self, surface):
        """ Allows the agents to draw on the game UI,
            Refer to the pygame reference to see how you can
            draw on a pygame.surface. The given surface is
            not cleared automatically. Additionally, this
            function will only be called when the renderer is
            active, and it will only be called for the active team.
        """
        import pygame
        
        # First agent clears the screen
        if self.id == 0:
            surface.fill((0,0,0,0))
        
        # Render cover spots
        y=0
        for row in coverSpotsMap:
            x = 0
            for val in row:
                if val != 0:
                    pass
                    #print "Drawing: " + str(x) +  " " + str(y)
                    #pygame.draw.circle(surface, (255,0,0), (x * 16 + 8, y * 16 + 8), 6,1)  
                x += 1
            y += 1

        for spot in self.__class__.campSpots:
            pygame.draw.circle(surface, (0,0,255), (spot[1][0][1]*16+8,spot[1][0][0]*16+8), 4,2)
        for spot in self.__class__.ammoCampSpots:
            pygame.draw.circle(surface, (0,255,0), (spot[0],spot[1]), 4,2)

        y=0
        for row in self.__class__.crowdedness:
            x = 0
            for val in row:
                if val != 0:
                    #print "Drawing: " + str(x) +  " " + str(y)
                    pygame.draw.circle(surface, (255,255,0), (x * 16 + 8, y * 16 + 8), val,1)  
                x += 1
            y += 1

    def finalize(self, interrupted=False):
        """ This function is called after the game ends, 
            either due to time/score limits, or due to an
            interrupt (CTRL+C) by the user. Use it to
            store any learned variables and write logs/reports.
        """
        pass
   
   
        
    
# Dummy class. Edit the class it derives from to choose your class
class Agent(__AUTOREPLACEME__):
    NAME = "Cyno_Paintrain"
    pass
 


